package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.apis.article.IArticleClient;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.exception.CustomException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
@Slf4j
public class WmNewsAutoScanServiceImpl  implements WmNewsAutoScanService {

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private GreenTextScan greenTextScan;

    @Autowired
    private GreenImageScan greenImageScan;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private IArticleClient iArticleClient;

    @Autowired
    private WmUserMapper wmUserMapper;


    @Autowired
    private WmChannelMapper wmChannelMapper;

    @Autowired
    private WmSensitiveMapper  wmSensitiveMapper;

    @Autowired
    private Tess4jClient tess4jClient;

    /**
     * 压根就是另外一个需求，不需要和提交文章业务同时执行
     * 自动审核接口
     * @param id 自媒体文章ID
     */
    @Override
    @Async //spring异步 基于线程池执行  不能有返回值（runable  run方法）
    public void autoScanWmNews(Integer id){
        //todo 1.校验参数
        if(id == null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews wmNews = wmNewsMapper.selectById(id);
        if(wmNews == null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //todo 1.5 抽取所有的图片和文本道一个map中，方便审核
        Map<String,Object> maps = handleTextAndImages(wmNews);
        //todo 1.6 抽取图片中的文本数据
        //0.构建一个变量存储图片中的文字
        StringBuffer sb = new StringBuffer();
        //1.获得所有图片
        List<String> imgList = (List<String>) maps.get("images");
        //2.图片去重
        imgList = imgList.stream().distinct().collect(Collectors.toList());
        //3.循环读取图片中文字即可
        for (String img : imgList) {
            //3.1 下载图片
            byte[] bytes = fileStorageService.downLoadFile(img);
            //3.2 把字节数组转换成一个BufferedImage对象
            ByteArrayInputStream is = new ByteArrayInputStream(bytes);
            BufferedImage imgFile = null;
            try {
                imgFile = ImageIO.read(is);
                String s = tess4jClient.doOCR(imgFile);
                sb.append(s+"-");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //4.把图片中的文字，存放回Map中
        maps.put("content",maps.get("content")+sb.toString());
        //todo 自敏感词匹配 （优化2.0）
        //*************************************************
        //先进行一个自铭感词的审核 (优化)
        //1.查询敏感词表中的所有数据
        List<WmSensitive> wmSensitiveList = wmSensitiveMapper.selectList(null);
        List<String> wmSensitives = wmSensitiveList.stream()
                .map(c ->c.getSensitives()).collect(Collectors.toList());
        //2.根据DFA工具类生成一个算法表
        SensitiveWordUtil.initMap(wmSensitives);
        //3.使用文本去匹配敏感词数据
        Map<String, Integer> content = SensitiveWordUtil.matchWords((String) maps.get("content"));
        if(content.size() > 0){
            //修改文章状态，拒绝原因
            wmNews.setStatus((short)2);
            wmNews.setReason("敏感词违规");
            wmNewsMapper.updateById(wmNews);
            return;
        }
        //*************************************************
        //todo 2. 审核文本
        boolean textFlag = handleTextScan(wmNews, (String) maps.get("content"));
        if(!textFlag){
            return;
        }
        //todo 3. 审核图片
        boolean imgFlag = handlerImagesScan(wmNews, (List<String>)maps.get("images"));
        if(!imgFlag){
            return;
        }
        //todo 4. 审核通过，保存app端相关文章数据
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(wmNews,articleDto);
        //todo 4.1 封装作者信息
        articleDto.setAuthorId(wmNews.getUserId().longValue());
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        articleDto.setAuthorName(wmUser.getName());
        //todo 4.2 分类名字
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if(wmChannel != null ){
            articleDto.setChannelName(wmChannel.getName());
        }
        //todo 4.3 修改文章的布局
        articleDto.setLayout(wmNews.getType());
        //todo 4.4 设置文章ID (新增是没有文章ID)
        if(wmNews.getArticleId() != null){
            articleDto.setId(wmNews.getArticleId());
        }
        //问题2：feign再进行远程调用如果超过1秒，则会出现超时异常 ，     空气开关
        //熔断器：保护服务安全的。熔断
        ResponseResult responseResult = iArticleClient.saveArticle(articleDto);
        //todo 5. 修改自媒体文章审核状体，结束  （把审核成功后的文章的ID存储道自媒体表中）
        if(responseResult.getCode() != 200){
            throw new RuntimeException("请求失败。。。。。");
        }
        wmNews.setArticleId((Long)responseResult.getData());
        wmNews.setStatus((short)9);
        wmNews.setReason("审核成功");
        wmNewsMapper.updateById(wmNews);
    }
    /**
     * 审核图片
     */
    private boolean handlerImagesScan(WmNews wmNews,List<String> images){//http://192.168.200.130:9000/1.jpg
        //处理一下图片
        //1.重复的图片去重
        images = images.stream().distinct().collect(Collectors.toList());
        //2.把图片从minio上下载下来变成字节数据
        List<byte[]> imageList = new ArrayList<>();
        images.stream().forEach(c->{
            imageList.add(fileStorageService.downLoadFile(c));
        });
        //3.开始审核
        try {
            Map map = greenImageScan.imageScan(imageList);
            if(map.get("suggestion").equals("block")){
                //文本违规  修改文章状态
                wmNews.setReason("图片违规");
                wmNews.setStatus((short)2);
                wmNewsMapper.updateById(wmNews);
                return false;
            }
            if(map.get("suggestion").equals("review")){
                //阿里云不能确定，转人工审核
                wmNews.setStatus((short)3);
                wmNewsMapper.updateById(wmNews);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }



    /**
     * 审核文本数据
     */
    private boolean handleTextScan(WmNews wmNews,String content){
        Map map = null;
        try {
            map = greenTextScan.greeTextScan(content);
            if(map.get("suggestion").equals("block")){
                //文本违规  修改文章状态
                wmNews.setReason("文本违规");
                wmNews.setStatus((short)2);
                wmNewsMapper.updateById(wmNews);
                return false;
            }
            if(map.get("suggestion").equals("review")){
                //阿里云不能确定，转人工审核
                wmNews.setStatus((short)3);
                wmNewsMapper.updateById(wmNews);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }



    /**
     * 根据自媒体文章对象，抽取文章中所有图片和文本道map中
     */
    private Map<String,Object> handleTextAndImages(WmNews wmNews){
        //0. 构建两个东西来存储图片和文字
        List<String> imgs = new ArrayList<>();
        StringBuffer sb = new StringBuffer();
        //1.从文章内容中抽取文字和图片   [{"type":"text",value:"xxxx"}]
        List<Map> maps = JSON.parseArray(wmNews.getContent(), Map.class);
        //2.遍历找值
        for (Map map : maps) {
            if(map.get("type").equals("text")){
                sb.append(map.get("value"));
            }
            if(map.get("type").equals("image")){
                imgs.add((String)map.get("value"));
            }
        }
        //3.提取文章封面的图片   http://1.jpg,http://2.jpg
        //http://192.168.200.130:9000/leadnews/2021/04/26/ef3cbe458db249f7bd6fb4339e593e55.jpg,http://192.168.200.130:9000/leadnews/2022/08/29/d3e7bf038a184f6c9582337aa0356616.jpg,http://192.168.200.130:9000/leadnews/2022/07/03/62bc542b3ad04e10bc273dda11b6fedf.jpg
        if(StringUtils.isNotBlank(wmNews.getImages())){
            String[] split = wmNews.getImages().split(",");
            imgs.addAll(Arrays.asList(split));
        }
        //4.提取标题中的文本和标签中的文本
        sb.append(wmNews.getTitle());
        sb.append(wmNews.getLabels());
        //5.封装返回结果
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("content",sb.toString());
        resultMap.put("images",imgs);
        return resultMap;
    }
}
